package com.denlaku.longan.util;

import com.denlaku.longan.SpringContext;
import com.denlaku.longan.core.BeanNames;
import com.denlaku.longan.core.Current;
import com.denlaku.longan.core.RequestContext;
import com.denlaku.longan.core.RequestContextManager;
import lombok.SneakyThrows;

import java.util.concurrent.Callable;
import java.util.concurrent.ThreadPoolExecutor;

/**
 * @author tianx
 */
public class ThreadPoolUtil {
    public static ThreadPoolExecutor getExecutor() {
        return SpringContext.getBean(ThreadPoolExecutor.class, BeanNames.IO_THREAD_POOL_EXECUTOR_NAME);
    }

    @SneakyThrows
    public static <V> V execSync(Callable<V> task) {
        RequestContext current = Current.getCurrent();
        Thread runsThread = Thread.currentThread();
        return getExecutor().submit(wrap(task, current, runsThread)).get();
    }

    public static void execAsync(Runnable task) {
        RequestContext current = Current.getCurrent();
        Thread runsThread = Thread.currentThread();
        getExecutor().execute(wrap(task, current, runsThread));
    }

    private static <V> Callable<V> wrap(Callable<V> task, RequestContext context, Thread runsThread) {
        return () -> {
            boolean isRunsThread = Thread.currentThread() == runsThread;
            try {
                RequestContextManager.setCurrent(context);
                return task.call();
            } finally {
                if (!isRunsThread) {
                    RequestContextManager.remove();
                }
            }
        };
    }

    private static Runnable wrap(Runnable task, RequestContext context, Thread runsThread) {
        return () -> {
            boolean isRunsThread = Thread.currentThread() == runsThread;
            try {
                RequestContextManager.setCurrent(context);
                task.run();
            } finally {
                if (!isRunsThread) {
                    RequestContextManager.remove();
                }
            }
        };
    }

}
